import 'dart:async';
import 'dart:math' as math;
import 'package:components/pull_down_refresh/controller.dart';
import 'package:components/pull_down_refresh/header.dart';
import 'package:flutter/material.dart';

/// 下拉刷新组件
class LaraPressPullRefresh extends StatefulWidget {
  final Widget Function(BuildContext context, ScrollPhysics physics) builder;
  final Future<void> Function()? onRefresh;
  final LaraPressRefreshController controller;
  final double triggerOffset;
  final double indicatorExtent;
  final Duration completeDelay;
  final ScrollPhysics? physics;

  const LaraPressPullRefresh({
    super.key,
    required this.builder,
    required this.controller,
    this.onRefresh,
    this.triggerOffset = 60,
    this.indicatorExtent = 80,
    this.completeDelay = const Duration(milliseconds: 600),
    this.physics,
  });

  /// 简化构造函数
  factory LaraPressPullRefresh.simple({
    required Widget Function(BuildContext, ScrollPhysics) builder,
    required Future<void> Function() onRefresh,
    double triggerOffset = 60,
    double indicatorExtent = 80,
  }) {
    return LaraPressPullRefresh(
      builder: builder,
      onRefresh: onRefresh,
      controller: LaraPressRefreshController(),
      triggerOffset: triggerOffset,
      indicatorExtent: indicatorExtent,
    );
  }

  @override
  State<LaraPressPullRefresh> createState() => _LaraPressPullRefreshState();
}

class _LaraPressPullRefreshState extends State<LaraPressPullRefresh> {
  double _dragOffset = 0.0;
  bool _pointerDown = false;

  LaraPressRefreshStatus get _status => widget.controller.status;
  double get _progress => (_dragOffset / widget.triggerOffset).clamp(0.0, 1.5);

  double get _visibleExtent {
    switch (_status) {
      case LaraPressRefreshStatus.refreshing:
      case LaraPressRefreshStatus.completed:
      case LaraPressRefreshStatus.failed:
        return widget.indicatorExtent;
      default:
        return math.min(widget.indicatorExtent, _dragOffset);
    }
  }

  bool _atTop(ScrollMetrics m) =>
      m.pixels <= m.minScrollExtent + 0.5 && !m.outOfRange;

  @override
  void initState() {
    super.initState();
    widget.controller.addListener(_onStatusChanged);
  }

  @override
  void dispose() {
    widget.controller.removeListener(_onStatusChanged);
    super.dispose();
  }

  void _onStatusChanged() {
    if (!mounted) return;
    setState(() {
      if (_status == LaraPressRefreshStatus.refreshing) {
        _dragOffset = widget.indicatorExtent;
      } else if (_status == LaraPressRefreshStatus.idle) {
        _dragOffset = 0;
      }
    });

    // 自动重置逻辑
    if (_status == LaraPressRefreshStatus.completed ||
        _status == LaraPressRefreshStatus.failed) {
      _autoReset();
    }
  }

  Future<void> _autoReset() async {
    await Future.delayed(widget.completeDelay);
    if (mounted) {
      widget.controller.reset();
    }
  }

  void _startRefresh() {
    if (_status == LaraPressRefreshStatus.refreshing) return;
    widget.controller.setRefreshing();

    if (widget.onRefresh != null) {
      widget.onRefresh!();
    }
  }

  void _updateDragState(double offset) {
    if (offset >= widget.triggerOffset) {
      widget.controller.setArmed();
    } else if (offset > 0) {
      widget.controller.setPulling();
    } else {
      widget.controller.reset();
    }
  }

  void _maybeTriggerOnRelease() {
    if (_status == LaraPressRefreshStatus.armed) {
      _startRefresh();
    } else if (_status == LaraPressRefreshStatus.pulling) {
      widget.controller.reset();
    }
  }

  @override
  Widget build(BuildContext context) {
    final physics =
        widget.physics ??
        const AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics());

    return Listener(
      onPointerDown: (_) => _pointerDown = true,
      onPointerUp: (_) {
        _pointerDown = false;
        _maybeTriggerOnRelease();
      },
      child: NotificationListener<ScrollNotification>(
        onNotification: _handleNotification,
        child: Stack(
          children: [
            Transform.translate(
              offset: Offset(0, _visibleExtent),
              child: ScrollConfiguration(
                behavior: const _NoGlowBehavior(),
                child: widget.builder(context, physics),
              ),
            ),
            Positioned(
              top: 0,
              left: 0,
              right: 0,
              height: _visibleExtent,
              child: IgnorePointer(
                ignoring: true,
                child: Align(
                  alignment: Alignment.bottomCenter,
                  child: SizedBox(
                    height: widget.indicatorExtent,
                    child: Center(
                      child: LaraPressRefreshHeader(
                        status: _status,
                        progress: _progress,
                        visibleExtent: _visibleExtent,
                      ),
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  bool _handleNotification(ScrollNotification n) {
    if (_status == LaraPressRefreshStatus.refreshing) return false;

    final m = n.metrics;

    if (n is ScrollStartNotification) {
      if (_atTop(m)) _dragOffset = 0;
    } else if (n is OverscrollNotification) {
      if (n.overscroll < 0 && m.extentBefore == 0) {
        setState(() {
          _dragOffset += -n.overscroll;
        });
        _updateDragState(_dragOffset);
      }
    } else if (n is ScrollUpdateNotification) {
      if (m.pixels < m.minScrollExtent && _pointerDown) {
        final overscrollAmount = m.minScrollExtent - m.pixels;
        setState(() {
          _dragOffset = overscrollAmount;
        });
        _updateDragState(_dragOffset);
      } else if (_dragOffset > 0 &&
          m.pixels >= m.minScrollExtent &&
          !_pointerDown) {
        setState(() {
          _dragOffset = 0;
        });
        widget.controller.reset();
      }
    } else if (n is ScrollEndNotification) {
      _maybeTriggerOnRelease();
    }

    return false;
  }
}

class _NoGlowBehavior extends ScrollBehavior {
  const _NoGlowBehavior();
  @override
  Widget buildOverscrollIndicator(
    BuildContext context,
    Widget child,
    ScrollableDetails details,
  ) => child;
}
